#ifndef __M_MATCHER_H__
#define __M_MATCHER_H__


#include <list>
#include <mutex>
#include <condition_variable>
#include <memory>
#include <thread>
#include "util.hpp"
#include "room.hpp"
#include "db.hpp"
#include "online.hpp"

template <class T>
class match_queue {
private:
    /*使用list模拟队列，而不直接使用stl的queue，因为有额外的删除中间数据的需求*/
    std::list<T> _list;
    /*保证线程安全：互斥和同步*/
    std::mutex _mutex;
    std::condition_variable _cond;
public:
    int size() {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    bool empty() {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    /*入队数据，并唤醒线程*/
    void push(const T &data) {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    /*出队数据，由于在队列>=2时需要连续出队两次，所以阻塞线程的wait操作需要额外进行
     * 防止出现只出队1次的情况，保证要出队就连续出队两次，再阻塞线程*/
    bool pop(T &data) {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty() == true) {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }
    /*阻塞线程*/
    void wait() {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    /*移除指定的数据*/
    void remove(const T &data) {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }

};

class matcher {
private:
    /*三个不同天梯等级的阻塞队列 uid */    
    match_queue<uint64_t> _q_bronze;
    match_queue<uint64_t> _q_silver;
    match_queue<uint64_t> _q_gold;
    /*访问对应阻塞队列的线程-消费者*/
    std::thread _th_bronze;
    std::thread _th_silver;
    std::thread _th_gold;
    /*房间管理*/
    room_manager *_rm;
    /*数据管理*/
    user_table *_ut;
    /*在线用户管理*/
    online_manager *_om;
private:
    void handle_match(match_queue<uint64_t> &mq) {
            while(1) {
                //1. 判断队列人数是否大于2，<2则阻塞等待
                while (mq.size() < 2) {
                    mq.wait();
                }    
                //2. 走下来代表人数够了，出队两个玩家
                uint64_t uid1, uid2;
                bool ret = mq.pop(uid1);
                if (ret == false) { 
                    continue; 
                }
                ret = mq.pop(uid2);
                if (ret == false) { 
                    this->add(uid1); 
                    continue; 
                }
                //3. 校验两个玩家是否在线，如果有人掉线，则要吧另一个人重新添加入队列
                wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
                if (conn1.get() == nullptr) {
                    this->add(uid2); 
                    continue;
                }
                wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
                if (conn2.get() == nullptr) {
                    this->add(uid1); 
                    continue;
                }
                //4. 为两个玩家创建房间，并将玩家加入房间中
                room_ptr rp = _rm->create_room(uid1, uid2);
                if (rp.get() == nullptr) {
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }
                //5. 对两个玩家进行响应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                json_util::serialize(resp, body);
                conn1->send(body);
                conn2->send(body);
            }
        }
    void th_bronze_entry() { return handle_match(_q_bronze); }
    void th_silver_entry() { return handle_match(_q_silver); }
    void th_gold_entry() { return handle_match(_q_gold); }
public:
    matcher(room_manager *rm, user_table *ut, online_manager *om) 
        : _rm(rm), _ut(ut), _om(om),
        _th_bronze(std::thread(&matcher::th_bronze_entry, this)),
        _th_silver(std::thread(&matcher::th_silver_entry, this)),
        _th_gold(std::thread(&matcher::th_gold_entry, this)) {
        DLOG("游戏匹配模块初始化成功!");
    }
    ~matcher() {
        DLOG("游戏匹配模块销毁成功!");
    }
    bool add(uint64_t uid) {
        // 1.获取玩家信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false){
            DLOG("获取玩家信息失败!");
            return false;
        }
        int score = user["score"].asInt();
        // 2.根据天梯分数把玩家uid加入对应匹配队列
        if(score < 2000){
            _q_bronze.push(uid);
        }
        else if(score < 3000){
            _q_silver.push(uid);
        }
        else{
            _q_gold.push(uid);
        }
        return true;
    }
    bool del(uint64_t uid) {
        // 1.获取玩家信息
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false){
            DLOG("获取玩家信息失败!");
            return false;
        }
        int score = user["score"].asInt();
        // 2.根据天梯分数把玩家uid移出对应匹配队列
        if(score < 2000){
            _q_bronze.remove(uid);
        }
        else if(score < 3000){
            _q_silver.remove(uid);
        }
        else{
            _q_gold.remove(uid);
        }
        return true;
    }
};

#endif
